package logic

import (
	"context"
	"errors"
	"exchange/internal/domain"
	"exchange/internal/model"
	"exchange/internal/svc"
	"fmt"
	"github.com/jinzhu/copier"
	"github.com/zeromicro/go-zero/core/logx"
	"grpc-common/exchange/types/order"
	"grpc-common/market/types/market"
	"grpc-common/ucenter/types/asset"
	"grpc-common/ucenter/types/member"
	"mscoin-common/msdb"
	"mscoin-common/msdb/tran"
)

type ExchangeOrderLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
	exchangeOrderDomain *domain.ExchangeOrderDomain
	transaction         tran.Transaction
	kafkaDomain         *domain.KafkaDomain
}

func NewExchangeOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ExchangeOrderLogic {
	orderDomain := domain.NewExchangeOrderDomain(svcCtx.Db)
	return &ExchangeOrderLogic{
		ctx:                 ctx,
		svcCtx:              svcCtx,
		Logger:              logx.WithContext(ctx),
		transaction:         tran.NewTransaction(svcCtx.Db.Conn),
		exchangeOrderDomain: orderDomain,
		kafkaDomain:         domain.NewKafkaDomain(svcCtx.KafkaClient, orderDomain),
	}
}

// 添加委托订单(重要....)
/***
- 判断参数是否合法
- 判断钱是否足够
- 创建订单，得到订单id和需要冻结的钱
- 发送消息到kafka
- ucenter接收消息，处理钱包冻结
- 失败则调用订单服务取消订单
*/
func (l *ExchangeOrderLogic) Add(req *order.OrderReq) (*order.AddOrderRes, error) {
	//1 判断基本的参数的信息 price和Amount
	if req.Type == model.TypeMap[model.LimitPrice] && req.Price <= 0 {
		return nil, errors.New("限价模式下价格不能小于等于0")
	}
	if req.Amount <= 0 {
		return nil, errors.New("数量不能小于等于0")
	}
	//2 查询member信息
	mem, err := l.svcCtx.MemberRpc.FindMemberById(l.ctx, &member.MemberReq{
		MemberId: req.UserId,
	})
	if err != nil {
		return nil, errors.New("查询不到当前用户信息")
	}
	if mem.TransactionStatus == 0 {
		return nil, errors.New("此用户被禁止交易")
	}
	//3 查询exchangeCoin信息，查询当前交易对=>是否可交易或者启用
	exchangeCoin, err := l.svcCtx.MarketRpc.FindSymbolInfo(l.ctx, &market.MarketReq{
		Symbol: req.Symbol,
	})
	if err != nil {
		return nil, errors.New("nonsupport exchangecoin")
	}
	if exchangeCoin.Exchangeable != 1 && exchangeCoin.Enable != 1 {
		return nil, errors.New("coin forbidden")
	}
	//4 查询需要被交易的货币的信息，买的话查询基准币，卖的话查询交易币
	//基准币 BTC/USDT
	baseSymbol := exchangeCoin.GetBaseSymbol()
	//交易币
	coinSymbol := exchangeCoin.GetCoinSymbol()
	cc := baseSymbol
	if req.Direction == model.DirectionMap[model.SELL] {
		//根据交易币查询
		cc = coinSymbol
	}
	coin, err := l.svcCtx.MarketRpc.FindCoinInfo(l.ctx, &market.MarketReq{
		Unit: cc,
	})
	if err != nil || coin == nil {
		return nil, errors.New("nonsupport coin")
	}
	//5 判断exchange_coin下的交易参数限制的信息
	///5-1基本的成交额，成交数量的限制
	if req.Type == model.TypeMap[model.MarketPrice] && req.Direction == model.DirectionMap[model.BUY] {
		if exchangeCoin.GetMinTurnover() > 0 && req.Amount < float64(exchangeCoin.GetMinTurnover()) {
			return nil, errors.New("成交额至少是" + fmt.Sprintf("%d", exchangeCoin.GetMinTurnover()))
		}
	} else {
		if exchangeCoin.GetMaxVolume() > 0 && exchangeCoin.GetMaxVolume() < req.Amount {
			return nil, errors.New("数量超出" + fmt.Sprintf("%f", exchangeCoin.GetMaxVolume()))
		}
		if exchangeCoin.GetMinVolume() > 0 && exchangeCoin.GetMinVolume() > req.Amount {
			return nil, errors.New("数量不能低于" + fmt.Sprintf("%f", exchangeCoin.GetMinVolume()))
		}
	}
	////5-2限价情况下卖的时候价格必须大于最低的售价，买的时候必须小于最高的买价;市价的话则不用限制
	if req.Direction == model.DirectionMap[model.SELL] && exchangeCoin.GetMinSellPrice() > 0 {
		if req.Price < exchangeCoin.MinSellPrice || req.Type == model.TypeMap[model.MarketPrice] {
			return nil, errors.New("不能低于最低限价:" + fmt.Sprintf("%f", exchangeCoin.GetMinSellPrice()))
		}
	}
	if req.Direction == model.DirectionMap[model.BUY] && exchangeCoin.GetMaxBuyPrice() > 0 {
		if req.Price > exchangeCoin.GetMaxBuyPrice() || req.Type == model.TypeMap[model.MarketPrice] {
			return nil, errors.New("不能低于最高限价:" + fmt.Sprintf("%f", exchangeCoin.GetMaxBuyPrice()))
		}
	}
	////5-3市价买卖是否开启
	if req.Type == model.TypeMap[model.MarketPrice] {
		if req.Direction == model.DirectionMap[model.BUY] && exchangeCoin.EnableMarketBuy == 0 {
			return nil, errors.New("不支持市价购买")
		}
		if req.Direction == model.DirectionMap[model.SELL] && exchangeCoin.EnableMarketSell == 0 {
			return nil, errors.New("不支持市价购买")
		}
	}
	//6查询钱包的信息是否合法足够(基准币和交易币)
	//6-1用户基准币的钱包是否合法
	baseWallet, err := l.svcCtx.AssetRpc.FindWalletBySymbol(l.ctx, &asset.AssetReq{
		CoinName: baseSymbol,
		UserId:   req.UserId,
	})
	if err != nil {
		return nil, errors.New("no baseWallet")
	}
	//6-1用户交易币的钱包是否合法
	exCoinWallet, err := l.svcCtx.AssetRpc.FindWalletBySymbol(l.ctx, &asset.AssetReq{
		CoinName: coinSymbol,
		UserId:   req.UserId,
	})
	if err != nil {
		return nil, errors.New("no coinWallet")
	}
	if baseWallet.IsLock == 1 || exCoinWallet.IsLock == 1 {
		return nil, errors.New("wallet locked")
	}
	//7是否满足委托数量
	count, err := l.exchangeOrderDomain.FindCurrentTradingCount(l.ctx, req.UserId, req.Symbol, req.Direction)
	if err != nil {
		logx.Info("查询委托数量失败")
		return nil, err
	}
	if exchangeCoin.GetMaxTradingOrder() > 0 && count >= exchangeCoin.GetMaxTradingOrder() {
		return nil, errors.New("超过最大挂单数量 " + fmt.Sprintf("%d", exchangeCoin.GetMaxTradingOrder()))
	}
	//8生成委托订单
	exchangeOrder := model.NewOrder()
	exchangeOrder.MemberId = req.UserId
	exchangeOrder.Symbol = req.Symbol
	exchangeOrder.BaseSymbol = baseSymbol
	exchangeOrder.CoinSymbol = coinSymbol
	typeCode := model.TypeMap.Code(req.Type)
	exchangeOrder.Type = typeCode
	directionCode := model.DirectionMap.Code(req.Direction)
	exchangeOrder.Direction = directionCode
	if exchangeOrder.Type == model.MarketPrice {
		exchangeOrder.Price = 0
	} else {
		exchangeOrder.Price = req.Price
	}
	exchangeOrder.UseDiscount = "0"
	exchangeOrder.Amount = req.Amount
	//9订单生成，发送消息到队列
	l.transaction.Action(func(conn msdb.DbConn) error {
		money, err := l.exchangeOrderDomain.AddOrder(l.ctx, conn, exchangeOrder, exchangeCoin, baseWallet, exCoinWallet)
		if err != nil {
			return errors.New("订单提交失败")
		}
		//发送kafka消息，订单创建成功，钱包该冻结钱了
		err = l.kafkaDomain.SendOrderAdd(
			"add-exchange-order",
			req.UserId,
			exchangeOrder.OrderId,
			money,
			req.Symbol,
			exchangeOrder.Direction,
			baseSymbol,
			coinSymbol,
		)
		if err != nil {
			return errors.New("发消息失败")
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &order.AddOrderRes{
		OrderId: exchangeOrder.OrderId,
	}, nil
}

// 取消委托
func (l *ExchangeOrderLogic) CancelOrder(req *order.OrderReq) (*order.CancelOrderRes, error) {
	err := l.exchangeOrderDomain.UpdateStatusCancel(l.ctx, req.OrderId)
	return &order.CancelOrderRes{
		OrderId: req.OrderId,
	}, err
}

// 根据id查询订单
func (l *ExchangeOrderLogic) FindByOrderId(req *order.OrderReq) (*order.ExchangeOrderOrigin, error) {
	exchangeOrder, err := l.exchangeOrderDomain.FindOrderByOrderId(l.ctx, req.OrderId)
	if err != nil {
		return nil, err
	}
	oo := &order.ExchangeOrderOrigin{}
	err = copier.Copy(oo, exchangeOrder)
	if err != nil {
		logx.Errorf("FindByOrderId copy error:%v", err)
		return nil, err
	}
	return oo, nil
}

// 查询当前委托订单
func (l *ExchangeOrderLogic) FindOrderCurrent(req *order.OrderReq) (*order.OrderRes, error) {
	currentList, total, err := l.exchangeOrderDomain.FindOrderCurrent(l.ctx, req.Symbol, req.Page, req.PageSize, req.UserId)
	if err != nil {
		return &order.OrderRes{}, nil
	}
	resList := []*order.ExchangeOrder{}
	err = copier.Copy(&resList, currentList)
	if err != nil {
		logx.Error("FindOrderCurrent copy error")
		return &order.OrderRes{}, nil
	}
	return &order.OrderRes{
		List:  resList,
		Total: total,
	}, nil
}

// 查看历史委托订单
func (l *ExchangeOrderLogic) FindOrderHistory(req *order.OrderReq) (*order.OrderRes, error) {
	historyList, total, err := l.exchangeOrderDomain.FindOrderHistory(l.ctx, req.Symbol, req.Page, req.PageSize, req.UserId)
	if err != nil {
		return &order.OrderRes{}, nil
	}
	resList := []*order.ExchangeOrder{}
	err = copier.Copy(&resList, historyList)
	if err != nil {
		logx.Error("FindOrderHistory copy error")
		return &order.OrderRes{}, nil
	}
	return &order.OrderRes{
		List:  resList,
		Total: total,
	}, nil
}
